package cn.sylinx.hbatis.ext.starter.common;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import cn.sylinx.hbatis.db.dialect.DbType;
import cn.sylinx.hbatis.ds.DataSourceWrapper;
import cn.sylinx.hbatis.ext.mirage.plugin.SqlResourcePreloadPlugin;
import cn.sylinx.hbatis.ext.starter.pool.DataSourceWrapperFactory;
import cn.sylinx.hbatis.kit.ClassUtil;
import cn.sylinx.hbatis.kit.StrKit;
import cn.sylinx.hbatis.log.GLog;
import cn.sylinx.hbatis.plugin.IPlugin;
import cn.sylinx.hbatis.plugin.cache.GuavaCacheConfig;
import cn.sylinx.hbatis.plugin.cache.GuavaCachePlugin;
import cn.sylinx.hbatis.plugin.datasource.DataSourcePlugin;
import cn.sylinx.hbatis.plugin.debug.DebugPlugin;
import cn.sylinx.hbatis.plugin.model.ModelPreloadPlugin;
import cn.sylinx.hbatis.plugin.proxy.ProxyPlugin;
import cn.sylinx.hbatis.plugin.statement.StatementHandlerPlugin;
import cn.sylinx.hbatis.plugin.transaction.TransactionIsolationPlugin;

class PluginBuilder {

	private List<IPlugin> pluginList = new ArrayList<IPlugin>();
	private EnvironmentWrapper env;
	private DataSource dataSource;

	public PluginBuilder(EnvironmentWrapper env) {
		this.env = env;
	}

	public PluginBuilder(EnvironmentWrapper env, DataSource dataSource) {
		this.env = env;
		this.dataSource = dataSource;
	}

	public PluginBuilder dataSource(DataSource dataSource) {
		this.dataSource = dataSource;
		return this;
	}

	private RelaxedPropertyResolverWrapper createRelaxedPropertyResolver(String prefix) {
		return new CommonRelaxedPropertyResolverWrapper(env, prefix);
	}

	/**
	 * 初始化modelpreload插件
	 * 
	 * @param env
	 */
	public PluginBuilder initModelPreLoadPlugin() {
		// scanPackageList

		RelaxedPropertyResolverWrapper propertyResolver = createRelaxedPropertyResolver("hbatis.plugin.modelpreload.");
		String inited = propertyResolver.getProperty("inited");
		if (!"true".equalsIgnoreCase(inited)) {
			return this;
		}

		GLog.debug("ModelPreloadPlugin inited.");
		ModelPreloadPlugin mp = new ModelPreloadPlugin();

		String acmStrategy = propertyResolver.getProperty("acmStrategy");
		if (StrKit.isNotBlank(acmStrategy)) {
			// 设置全局模型字段映射策略
			mp.setAcmStrategyClass(acmStrategy);
		}

		String scanPackageList = propertyResolver.getProperty("scanPackageList");
		if (StrKit.isNotBlank(scanPackageList)) {
			GLog.debug("ModelPreloadPlugin scanPackageList:{}", scanPackageList);
			String[] pkgArray = scanPackageList.split(",");
			mp.setScanPackageList(Arrays.asList(pkgArray));
		}

		pluginList.add(mp);
		return this;
	}

	public PluginBuilder initSqlResourcePreloadPlugin() {

		RelaxedPropertyResolverWrapper propertyResolver = createRelaxedPropertyResolver("hbatis.plugin.mirage.");
		String path = propertyResolver.getProperty("sqlpath");
		if (StrKit.isBlank(path)) {
			return this;
		}

		GLog.debug("SqlResourcePreloadPlugin inited.");
		String[] pathArray = path.split(",");
		List<String> pathList = Arrays.asList(pathArray);
		SqlResourcePreloadPlugin plugin = new SqlResourcePreloadPlugin(pathList);
		pluginList.add(plugin);
		return this;
	}

	/**
	 * 添加sql语句处理插件
	 * 
	 * @param p
	 */
	public PluginBuilder initStatementHandlerPlugin() {

		RelaxedPropertyResolverWrapper propertyResolver = createRelaxedPropertyResolver("hbatis.plugin.statement.");
		String statementHandlerClass = propertyResolver.getProperty("clazz");
		if (StrKit.isBlank(statementHandlerClass)) {
			return this;
		}

		GLog.debug("StatementHandlerPlugin inited.");
		StatementHandlerPlugin shp = new StatementHandlerPlugin();
		shp.setStatementHandlerClass(statementHandlerClass);
		pluginList.add(shp);
		return this;
	}

	public PluginBuilder initTransactionPlugin() {
		// TransactionIsolationPlugin
		RelaxedPropertyResolverWrapper propertyResolver = createRelaxedPropertyResolver("hbatis.plugin.transaction.");
		String level = propertyResolver.getProperty("level");
		if (StrKit.isBlank(level)) {
			return this;
		}

		GLog.debug("TransactionIsolationPlugin inited.");
		int levelInt = Integer.parseInt(level);
		TransactionIsolationPlugin plugin = new TransactionIsolationPlugin(levelInt);
		pluginList.add(plugin);
		return this;
	}

	public PluginBuilder initDebugPlugin() {
		DebugPlugin debugPlugin = new DebugPlugin();
		String debugStr = env.getProperty("hbatis.debug", "false");
		boolean debug = Boolean.valueOf(debugStr);
		debugPlugin.setDebug(debug);
		pluginList.add(debugPlugin);
		return this;
	}

	public PluginBuilder initCachePlugin() {

		RelaxedPropertyResolverWrapper propertyResolver = createRelaxedPropertyResolver("hbatis.plugin.cache.");
		boolean useCache = Boolean.valueOf(propertyResolver.getProperty("inited", "false"));
		if (!useCache) {
			return this;
		}

		boolean isClassExist = ClassUtil.isClassExist("com.google.common.cache.CacheBuilder");
		if (!isClassExist) {
			GLog.error("guava依赖库没有加载，缓存插件无法初始化");
			return this;
		}

		// hbatis.plugin.cache.expire=5, 过期时间，分钟
		// hbatis.plugin.cache.maxSize=2000, 最大个数缓存键
		String expire = propertyResolver.getProperty("expire", "5");
		String maxSize = propertyResolver.getProperty("maxSize", "2000");
		long expireLong = 5;
		long maxSizeLong = 2000;

		try {
			expireLong = Long.parseLong(expire);
			maxSizeLong = Long.parseLong(maxSize);
		} catch (Exception e) {
			GLog.error("cache config error, use default", e);
		}

		GuavaCacheConfig gcc = new GuavaCacheConfig();
		gcc.setExpireAfterWrite(expireLong);
		gcc.setMaximumSize(maxSizeLong);

		GLog.debug("cache config, expireAfterWrite:{}, maximumSize:{}", expireLong, maxSizeLong);
		pluginList.add(new GuavaCachePlugin(gcc));
		return this;
	}

	// 初始化代理插件
	public PluginBuilder initProxyPlugin() {

		// TransactionIsolationPlugin
		RelaxedPropertyResolverWrapper propertyResolver = createRelaxedPropertyResolver("hbatis.plugin.proxy.");
		String clzList = propertyResolver.getProperty("class");
		if (StrKit.isBlank(clzList)) {
			return this;
		}

		ProxyPlugin plugin = new ProxyPlugin();
		String[] clzArray = clzList.split(",");
		for (String clz : clzArray) {
			if (StrKit.isNotBlank(clz)) {
				IPlugin item = null;
				try {
					item = (IPlugin) Class.forName(clz.trim()).newInstance();
				} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
					GLog.error("初始化插件失败, clz: " + clz, e);
				}
				if (item != null) {
					// 加入代理插件
					plugin.addPlugin(item);
				}
			}
		}

		pluginList.add(plugin);
		return this;
	}

	private DataSourceWrapper buildDataSource(Map<String, Object> dsMap, Map<String, Object> extConfig) {
		return DataSourceWrapperFactory.buildDataSource(dsMap, extConfig);
	}

	/**
	 * 初始化数据源plugin
	 * 
	 * @param env
	 */
	public PluginBuilder initDataSourcePlugin() {

		////// 读取主数据源开始
		DataSourcePlugin p = new DataSourcePlugin();
		DataSourceWrapper defaultDataSource = null;
		if (dataSource != null) {
			// 主数据源使用现有注入数据源
			String dbtype = env.getProperty("hbatis.ds.dbtype", DbType.MYSQL.getValue());
			defaultDataSource = DataSourceWrapper.create(dataSource, DbType.getDbType(dbtype));

		} else {

			RelaxedPropertyResolverWrapper propertyResolver = createRelaxedPropertyResolver(
					"hbatis.datasource.default.");
			Map<String, Object> dsMap = new HashMap<String, Object>();
			dsMap.put("url", propertyResolver.getProperty("url"));
			dsMap.put("driver", propertyResolver.getProperty("driver"));
			dsMap.put("username", propertyResolver.getProperty("username"));
			dsMap.put("password", propertyResolver.getProperty("password"));
			dsMap.put("dbtype", propertyResolver.getProperty("dbtype"));
			dsMap.put("pooltype", propertyResolver.getProperty("pooltype"));

			// 获取其它配置
			Map<String, Object> extConfig = propertyResolver.getSubProperties("config.");
			defaultDataSource = buildDataSource(dsMap, extConfig);
		}
		// 添加默认数据源
		p.setDataSourceWrapper(defaultDataSource);
		pluginList.add(p);
		/////// 主数据源初始化完毕 ////////

		////// 以下是多数据源初始化 //////////
		// 读取配置文件获取更多数据源，也可以通过defaultDataSource读取数据库获取更多数据源
		RelaxedPropertyResolverWrapper propertyResolverMulti = createRelaxedPropertyResolver(
				"hbatis.datasource.multi.");
		String dsPrefixs = propertyResolverMulti.getProperty("names");
		if (dsPrefixs == null || "".equals(dsPrefixs)) {
			return this;
		}
		for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
			Map<String, Object> dsMapMulti = propertyResolverMulti.getSubProperties(dsPrefix + ".");
			Map<String, Object> extConfigMulti = propertyResolverMulti.getSubProperties(dsPrefix + ".config.");
			DataSourceWrapper ds = buildDataSource(dsMapMulti, extConfigMulti);
			DataSourcePlugin ptmp = new DataSourcePlugin();
			ptmp.setJdbcResourceName(dsPrefix);
			ptmp.setDataSourceWrapper(ds);
			pluginList.add(ptmp);

		}
		////// 多数据源初始化完毕 //////////

		return this;
	}

	public List<IPlugin> build() {
		return this.pluginList;
	}
}
